'use strict';
let term_ns = new function() {
	const BASE_API = {
		Vector2, vec2, VectorN, vecN, EventEmitter, random, JSONcopy,
		Promise, Proxy, WeakRef,
		console, Date, Math, JSON, Set, Map, WeakSet, WeakMap,
		Object, Array, Function, Number, String, RegExp, BigInt, Symbol
	};
	
	
	let delay = (cb, time = 0) => new Promise(res => {
		let t = setTimeout(() => {
			clearTimeout(t);
			res(cb());
		}, time);
	});
	
	
	let File = class extends EventEmitter {
		constructor(filename, content) {
			super();
			
			this.name = filename;
			this.content = content;
		}
		
		read() {
			return this.content;
		}
	};
	
	
	let FileSystem = class extends EventEmitter {
		constructor(id) {
			super();
			if(!String(id)) throw Error('invalid id');
			
			this.id = id;
			
			let storage = window.localStorage.getItem(this.id);
			if(storage) this.files = JSON.parse(storage);
			else this.files = {};
			
			if(!FileSystem.fs[this.id]) {
				window.onbeforeunload = e => window.localStorage.setItem(this.id, JSON.stringify(this.files));
				FileSystem.fs[this.id] = 1;
			};
		}
		
		removeFileSync(filename) { return delete this.files[filename]; }
		removeFile(filename) {
			return new Promise((res, rej) => res(this.removeFileSync(filename)));
		}
		
		hasFileSync(filename) { return filename in this.files; }
		hasFile(filename) {
			return new Promise((res, rej) => res(this.hasFileSync(filename)));
		}
		
		
		readFileSync(filename) { return this.files[filename]; }
		readFile(filename) {
			return new Promise((res, rej) => res(this.readFileSync(filename)));
		}
		
		writeFileSync(filename, content = '') {
			if(typeof content !== 'string') throw Error('content is not a string');
			return this.files[filename] = content;
		}
		writeFile(filename, content = '') {
			return new Promise((res, rej) => res(this.writeFileSync(filename, content)));
		}
		
		static fs = {};
	};
	
	
	let Process = class extends EventEmitter {
		constructor(code, virenv, filepath) {
			super();
			
			let moduleCache = {};
			
			let require = name => {
				let module = null;
				
				if(name in moduleCache) module = moduleCache[name];
				else if(name in virenv.globalModules) module = virenv.globalModules[name]();
				else if(virenv.fs.hasFileSync(name)) {
					let processModule = new Process(virenv.fs.readFileSync(name), virenv, name);
					module = processModule.api.module.export;
				};
				
				moduleCache[name] = module;
				
				return module;
			};
			
			let module = {
				export: {},
				filepath: filepath
			};
			
			let process = {
				f: 2
			};
			
			
			let api = this.api = {
				process, require,
				module,
				
				...BASE_API
			};
			api.global = api;
			
			
			codeShell(code, api, { file: filepath }).call({});
		}
	};
	
	
	let Virtualenv = this.Virtualenv = class extends EventEmitter {
		constructor() {
			super();
			
			this.fs = new FileSystem('fs_storage');
			
			this.globalModules = {};
			this.globalModules.fs = () => {
				return this.fs;
			};
		}
		
		run(code, filename) {
			let process = new Process(code, this, filename);
		}
	};
	
	
	let Terminal = this.Terminal = class extends Virtualenv {
		constructor() {
			super();
		}
		
		cmd(cmd) {
			let cmds = cmd.split(/\s+/);
			console.group(cmds.join(' '));
			
			if(cmds[0] === 'run' && cmds[1]) {
				let code = this.fs.readFileSync(cmds[1]);
				
				this.run(code, cmds[1]);
			} else if(cmds[0] === 'write' && cmds[1]) {
				let file = globalThis.__main__;
				
				this.fs.writeFileSync(cmds[1], file);
			} else if(cmds[0] === 'rm' && cmds[1]) {
				this.fs.removeFileSync(cmds[1]);
			} else if(cmds[0] === 'has' && cmds[1]) {
				return this.fs.hasFileSync(cmds[1]);
			} else if(cmds[0] === 'ls') {
				console.log(JSON.stringify(this.fs.files));
				console.log(this.fs.files);
			};
			
			console.groupEnd();
		}
	};
};
